To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

TODO

  • get secret_id from last Combine

  • get last Combine, ideally during constructor

  • get last bond value, during constructor or intial_value

Maybe

Bonds.incorporate_last_bond(
	just_created::T, 
	previous::T, 
	previous_value_from_js::Any
)::T

with default (a,b)->a

👀 Reading hidden code
md"""
# TODO
- get secret_id from last Combine
- get last Combine, ideally during constructor
- get last bond value, during constructor or intial_value


Maybe

```julia
Bonds.incorporate_last_bond(
just_created::T,
previous::T,
previous_value_from_js::Any
)::T

```
with default `(a,b)->a`
"""
488 μs
Error message

UndefVarError: Slider not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. macro expansion
  2. Show more...
b = @bind x Slider(1:10);
👀 Reading hidden code
---
Error message

Another cell defining b contains errors.

Keep calm, you got this!
b
👀 Reading hidden code
---
# just_created_bond = Slider(1:10)

# bond = APD.Bonds.incorporate_last_bond(just_created_bond, old_bond)


# x = Bonds.initial_value(bond)

# # let Pluto server know that :x is bound to `bond`

# HTML("<pluto-bond for=x>$(bond)</pluto-bond>")
👀 Reading hidden code
9.3 μs
all_names = [:fons, :hannes, :asdf_dsfsdf]
👀 Reading hidden code
16.4 μs
Error message

UndefVarError: MultiCheckBox not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. macro expansion
  2. Show more...
¯\_(ツ)_/¯
@bind chosen_names MultiCheckBox(all_names)
👀 Reading hidden code
---
Error message

Another cell defining f and chosen_names contains errors.

Try asking on Julia Discourse!
@bind vals f(chosen_names) # (is een simpele PlutoUI.combine)
👀 Reading hidden code
---
Error message

Another cell defining f and chosen_names contains errors.

Maybe time for a break? ☕️
vals
👀 Reading hidden code
---
skip_as_script (generic function with 1 method)
👀 Reading hidden code
559 μs
@skip_as_script
@skip_as_script expression

Marks a expression as Pluto-only, which means that it won't be executed when running outside Pluto. Do not use this for your own projects.

👀 Reading hidden code
868 μs
@only_as_script (macro with 1 method)
macro only_as_script(ex) skip_as_script(__module__) ? nothing : esc(ex) end
👀 Reading hidden code
463 μs
import Pkg
👀 Reading hidden code
258 μs
Pkg.activate()
👀 Reading hidden code
❔
  Activating project at `~/.julia/environments/v1.7`
155 ms
Error message

The package PlutoUI.jl could not load because it failed to initialize.

That's not nice! Things you could try:

  • Restart the notebook.
  • Try a different Julia version.
  • Contact the developers of PlutoUI.jl about this error.

You might find useful information in the package installation log:

using PlutoUI
👀 Reading hidden code
---
Error message

The package HypertextLiteral.jl could not load because it failed to initialize.

That's not nice! Things you could try:

  • Restart the notebook.
  • Try a different Julia version.
  • Contact the developers of HypertextLiteral.jl about this error.

You might find useful information in the package installation log:

using HypertextLiteral
👀 Reading hidden code
---
struct A
x
y
end
👀 Reading hidden code
845 μs
false
A([1],2) == A([1],2)
👀 Reading hidden code
17.1 μs
begin
struct Parameters
names::Vector{Symbol}
end

function Base.show(io::IO, m::MIME"text/html", ps::Parameters)
end

end
👀 Reading hidden code
1.5 ms
Error message

UndefVarError: @htl not defined

TwoSliders(range; kwargs...) = PlutoUI.combine() do Child
@htl("$(Child(Slider(range; kwargs...)))$(Child(Slider(range; kwargs...)))")
end
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
function f(names)
# Parameters(names)
combine(try_persist_values=false) do Child
@htl("""
<table><caption><h3>Parameters</h3></caption>
<thead><tr><th>Name</th><th>value</th></thead>
<tbody>
$((@htl("""
<tr><td>$(name)</td><td>$(Child(name, Slider(0:.1:100)))</td></tr>
""") for name in names))</tbody></table>
""")
end
end
👀 Reading hidden code
---
Error message

UndefVarError: Scrubbable not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. Scrubbable(1:100) == Scrubbable(1:100)
Scrubbable(1:100) == Scrubbable(1:100)
👀 Reading hidden code
---
Error message

UndefVarError: NumberField not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. NumberField(1:100) == NumberField(1:100)
uhmmmmmm??!
NumberField(1:100) == NumberField(1:100)
👀 Reading hidden code
---
Error message

UndefVarError: Slider not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. Slider(1:100) == Slider(1:100)
Slider(1:100) == Slider(1:100)
👀 Reading hidden code
---
Error message

UndefVarError: Scrubbable not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. Scrubbable(1) == Scrubbable(1)
Scrubbable(1) == Scrubbable(1)
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
ca = combine() do Child
@htl("""
Hello $(Child(Slider(1:100)))
""")
end
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
cb = combine() do Child
@htl("""
Hello $(Child(Slider(1:100)))
""")
end
👀 Reading hidden code
---
Error message

Another cell defining ca and cb contains errors.

ca == cb
👀 Reading hidden code
---
Error message

Another cell defining ca and cb contains errors.

ca === cb
👀 Reading hidden code
---
Error message

Another cell defining ca and cb contains errors.

beep boop CRASH 🤖
hash(ca), hash(cb)
👀 Reading hidden code
---
Error message

UndefVarError: Dump not defined

Maybe time for a break? ☕️
Dump(ca)
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
ha = @htl("""ab $(@htl("<em style=$((a=123, b=234))>asdf</em>")) c
<script>
let z = $(1+1)
</script>

""")
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
You got this!
hb = @htl("""ab $(@htl("<em style=$((a=123, b=234))>asdf</em>")) c
<script>
let z = $(1+1)
</script>

""")
👀 Reading hidden code
---
Error message

Another cell defining hb and ha contains errors.

ha == hb
👀 Reading hidden code
---
Error message

Another cell defining hb and ha contains errors.

ha === hb
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
@htl("ab$(123)c").contents .== @htl("ab$(123)c").contents
👀 Reading hidden code
---
true
"a" == "a"
👀 Reading hidden code
11.8 μs
👀 Reading hidden code
66.2 μs
@bind asdfsdf html"<input>"
👀 Reading hidden code
238 ms
missing
sleep(1); asdfsdf
👀 Reading hidden code
1.0 s

Combine

👀 Reading hidden code
193 μs
Error message

The package AbstractPlutoDingetjes.jl could not load because it failed to initialize.

That's not nice! Things you could try:

  • Restart the notebook.
  • Try a different Julia version.
  • Contact the developers of AbstractPlutoDingetjes.jl about this error.

You might find useful information in the package installation log:

import AbstractPlutoDingetjes.Bonds
👀 Reading hidden code
---
Error message

The package AbstractPlutoDingetjes.jl could not load because it failed to initialize.

That's not nice! Things you could try:

  • Restart the notebook.
  • Try a different Julia version.
  • Contact the developers of AbstractPlutoDingetjes.jl about this error.

You might find useful information in the package installation log:

import AbstractPlutoDingetjes
👀 Reading hidden code
---

Combining bonds

👀 Reading hidden code
165 μs

The magic

👀 Reading hidden code
178 μs
Error message

Another cell defining HypertextLiteral contains errors.

const set_input_value_compat = HypertextLiteral.JavaScript("""
(() => {
let result = null
try {
result = setBoundElementValueLikePluto
} catch (e) {
result = ((input, new_value) => {
// fallback in case https://github.com/fonsp/Pluto.jl/pull/1755 is not available
if (new_value == null) {
//@ts-ignore
input.value = new_value
return
}
if (input instanceof HTMLInputElement) {
switch (input.type) {
case "range":
case "number": {
if (input.valueAsNumber !== new_value) {
input.valueAsNumber = new_value
}
return
}
case "date": {
if (input.valueAsDate == null || Number(input.valueAsDate) !== Number(new_value)) {
input.valueAsDate = new_value
}
return
}
case "checkbox": {
if (input.checked !== new_value) {
input.checked = new_value
}
return
}
case "file": {
// Can't set files :(
return
}
}
} else if (input instanceof HTMLSelectElement && input.multiple) {
for (let option of Array.from(input.options)) {
option.selected = new_value.includes(option.value)
}
return
}
//@ts-ignore
if (input.value !== new_value) {
//@ts-ignore
input.value = new_value
}
})
}
return result
})()
""")
👀 Reading hidden code
---
RenderCallback
RenderCallback(callback::Function, x::Any)

An HTML display passthrough of x (displays the same content), but when it is displayed, a callback function is invoked. disable_callback! can remove a callback.

👀 Reading hidden code
5.3 ms
Error message

UndefVarError: @htl not defined

Keep calm, you got this!
begin
Base.@kwdef struct CombinedBonds
display_content::Any
captured_names::Union{Nothing,NTuple{N,Symbol} where N}
captured_bonds::Vector{Any}
secret_key::String
try_persist_values::Bool
end

function Base.show(io::IO, m::MIME"text/html", cb::CombinedBonds)
if !AbstractPlutoDingetjes.is_supported_by_display(io, Bonds.transform_value)
return Base.show(io, m, HTML("<span>❌ You need to update Pluto to use this PlutoUI element.</span>"))
end
output = @htl(
"""<span style='display: contents;'>$(
cb.display_content
)<script id=$("plutoui-combine")>

let set_input_value = $(set_input_value_compat)
const div = currentScript.parentElement
let key = $(cb.secret_key)
const inputs = div.querySelectorAll(`pl-combined-child[key='\${key}'] > *:first-child`)

const names = $(cb.captured_names === nothing ? nothing : string.(cb.captured_names))
const values = Array(inputs.length)
const old_names = this?.names
const old_values = this?.values
const try_persist_values = $(cb.try_persist_values)

console.info("this ", {old_names, names, old_values, values})

try_persist_values && old_names != null && old_values != null && old_names.length === old_values.length && inputs.forEach((el,i) => {
let old_index = old_names.indexOf(names[i])

if(old_index !== -1){
set_input_value(el, old_values[old_index])
}
})
inputs.forEach(async (el,i) => {
el.oninput = (e) => {
e.stopPropagation()
}
const gen = Generators.input(el)
while(true) {
values[i] = await gen.next().value
div.dispatchEvent(new CustomEvent("input", {}))
}
})
Object.defineProperty(div, 'value', {
get: () => values,
set: (newvals) => {
if(!newvals) {
return
}
inputs.forEach((el, i) => {
values[i] = newvals[i]
set_input_value(el, newvals[i])
})
},
configurable: true,
});

const return_dummy = document.createElement("span")
return_dummy.names = names
return_dummy.values = values
return return_dummy

</script></span>""")
Base.show(io, m, output)
end

function Bonds.initial_value(cb::CombinedBonds)
vals = tuple((Bonds.initial_value(b) for b in cb.captured_bonds)...)

if cb.captured_names === nothing
vals
else
NamedTuple{cb.captured_names}(vals)
end
end
function Bonds.validate_value(cb::CombinedBonds, from_js)
if from_js isa Vector && length(from_js) == length(cb.captured_bonds)
all((
Bonds.validate_value(bond, val_js)
for (bond, val_js) in zip(cb.captured_bonds, from_js)
))
else
false
end
end
function Bonds.transform_value(cb::CombinedBonds, from_js)
@assert from_js isa Vector
@assert length(from_js) == length(cb.captured_bonds)

vals = tuple((
Bonds.transform_value(bond, val_js)
for (bond, val_js) in zip(cb.captured_bonds, from_js)
)...)

if cb.captured_names === nothing
vals
else
NamedTuple{cb.captured_names}(vals)
end
# [
# Bonds.transform_value(bond, val_js)
# for (bond, val_js) in zip(cb.captured_bonds, from_js)
# ]
end
# TODO:
# function Bonds.possible_values (cb::CombinedBonds, from_js)
# end


# function Bonds.incorporate_last_bond(
# new::CombinedBonds, old::CombinedBonds, old_from_js::Any
# )
# if (
# (new.captured_names !== nothing && old.captured_names !== nothing) &&
# (new.try_persist_values && old.try_persist_values)
# )
# for (new_i, name) in enumerate(new.captured_names)
# old_i = findfirst(isequal(name), old.captured_names)
# if old_i !== nothing
# new_bond = new.captured_bonds[new_i]
# old_bond = old.captured_bonds[odl_i]

# incorporate_last_bond(new_bond, old_bond, )
# end
# end
# common_names = new.captured_names ∩ old.captured_names
# else
# new
# end
# end
end
👀 Reading hidden code
---
render_hmtl_with_pluto_display_features

Same as repr(MIME"text/html"(), x) but the IOContext is set up to match the one used by Pluto to render. This means that if the object being rendered wants to use AbstractPlutoDingetjes.is_supported_by_display, they can.

"""
Same as `repr(MIME"text/html"(), x)` but the `IOContext` is set up to match the one used by Pluto to render. This means that if the object being rendered wants to use `AbstractPlutoDingetjes.is_supported_by_display`, they can.
"""
render_hmtl_with_pluto_display_features(x) = sprint() do io
Base.show(
IOContext(io,
:pluto_supported_integration_features => [
AbstractPlutoDingetjes,
AbstractPlutoDingetjes.Bonds,
AbstractPlutoDingetjes.Bonds.initial_value,
AbstractPlutoDingetjes.Bonds.transform_value,
AbstractPlutoDingetjes.Bonds.possible_values,
],
:color => false, :limit => true,
:displaysize => (18, 88), :is_pluto => true,
),
MIME"text/html"(),
x
)
end
👀 Reading hidden code
2.0 ms
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
function _combine(f::Function; try_persist_values::Bool=false)
key = String(rand('a':'z', 10))

captured_names = Symbol[]
captured_bonds = []

function combined_child_element(x)
@htl("""<pl-combined-child key=$(key) style='display: contents;'>$(x)</pl-combined-child>""")
end

created_callbacks = RenderCallback[]

#=
This is the function that will wrap around contained input elements.
Besides wrapping the input inside a special HTML element
(with `combined_child_element`), it also secretly adds the element to
`captured_bonds`.
This allows us to know exactly which elements are contained in the combine,
which we use for `Bonds.initial_value`, `Bonds.transform_value`, etc.
This function could be a lot simpler:

```
function Child(x)
push!(captured_bonds, x)
combined_child_element(x)
end
```
But instead, it's complicated!
The reason is that we want to capture the combined bonds not in the order
that they are wrapped (i.e. when `Child` is called), but in the order that they
are **displayed** in, because this matches the order that our JavaScript code
will find the special elements in.
=#
function Child(x)
rc = RenderCallback(combined_child_element(x)) do
# @info "Rendering" x stacktrace()
push!(captured_bonds, x)
end
push!(created_callbacks, rc)
rc
end
# the same, but with `name`
function Child(name::Union{String,Symbol}, x)
rc = RenderCallback(combined_child_element(x)) do
push!(captured_bonds, x)
push!(captured_names, Symbol(name))
end
push!(created_callbacks, rc)
rc
end

# call the user's render function
result = f(Child)

# Trigger HTML display, which will also render the Child elements, and fire our callbacks.
# We store the result because we don't want to re-render the contents every time the combine is rendered: if the displayed content contains lazy generators, then blablablalbl difficult but fixed now -fons
display_html = render_hmtl_with_pluto_display_features(result)

# @info "Captured" captured_bonds length(created_callbacks)
# disable callbacks
disable_callback!.(created_callbacks)
# lets see what we got
@assert isempty(captured_names) || length(captured_names) == length(captured_bonds) "Some children do not have a name. Make sure that all calls of `Child` provide two arguments."
CombinedBonds(;
secret_key = key,
try_persist_values = try_persist_values,
captured_names = isempty(captured_names) ? nothing : tuple(captured_names...),
captured_bonds = captured_bonds,
display_content = HTML(display_html),
)
end
👀 Reading hidden code
---
combine
PlutoUI.combine(render_function::Function)

Combine multiple input elements into one. The combined values are sent to @bind as a single tuple.

render_function is a function that you write yourself, take a look at the examples below.

Examples

🐶 & 🐱

We use the do syntax to write our render_function. The Child function is wrapped around each input, to let combine know which values to combine.

@bind values PlutoUI.combine() do Child
	md"""
	# Hi there!

	I have $(
		Child(Slider(1:10))
	) dogs and $(
		Child(Slider(5:100))
	) cats.

	Would you like to see them? $(Child(CheckBox(true)))
	"""
end

values == (1, 5, true) # (initially)

The output looks like:

screenshot of running the code above inside Pluto

🎏

The combine function is most useful when you want to generate your input elements dynamically. This example uses HypertextLiteral.jl for the @htl macro:

function wind_speeds(directions)
	PlutoUI.combine() do Child
		@htl("""
		<h6>Wind speeds</h6>
		<ul>
		$([
			@htl("<li>$(name): $(Child(Slider(1:100)))</li>")
			for name in directions
		])
		</ul>
		""")
	end
end

@bind speeds wind_speeds(["North", "East", "South", "West"])

speeds == (1, 1, 1, 1) # (initially)

# after moving the sliders:
speeds == (100, 36, 73, 60)

The output looks like:

screenshot of running the code above inside Pluto

Named variant

In the last example, we used Child to wrap around contained inputs:

Child(Slider(1:100))

We can also use the named variant, which looks like:

Child("East", Slider(1:100))

When you use the named variant for all children, the bound value will be NamedTuple, instead of a Tuple.

Let's rewrite our example to use the named variant:

function wind_speeds(directions)
	PlutoUI.combine() do Child
		@htl("""
		<h6>Wind speeds</h6>
		<ul>
		$([
			@htl("<li>$(name): $(Child(name, Slider(1:100)))</li>")
			for name in directions
		])
		</ul>
		""")
	end
end

@bind speeds wind_speeds(["North", "East", "South", "West"])

speeds == (North=1, East=1, South=1, West=1) # (initially)

# after moving the sliders:
speeds == (North=100, East=36, South=73, West=60)

md"The Eastern wind speed is $(speeds.East)."

The output looks like:

screenshot of running the code above inside Pluto

Why?

You can make a new widget!

You can use combine to create a brand new widget yourself, by combining existing ones!

In the example above, we created a new widget called wind_speeds. You could put this function in a package (e.g. WeatherUI.jl) and people could use it like so:

import WeatherUI: wind_speeds

@bind speeds wind_speeds(["North", "East"])

In other words: you can use combine to create application-specific UI elements, and you can put those in your package!

Difference with repeated @bind

The standard way to combine multiple inputs into one output is to use @bind multiple times. Our initial example could more easily be written as:

md"""
# Hi there!

I have $(@bind num_dogs Slider(1:10)) dogs and $(@bind num_cats Slider(5:10)) cats.

Would you like to see them? $(@bind want_to_see CheckBox(true))
"""

The combine function is useful when you are generating inputs dynamically, like in our second example. This is useful when:

  • The number of parameters is very large, and you don't want to write @bind parameter1 ..., @bind parameter2 ..., etc.

  • The number of parameters is dynamic! For example, you can load in a table in one cell, and then use combine in another cell to select which rows you want to use.

"""
```julia
PlutoUI.combine(render_function::Function)
```

Combine multiple input elements into one. The combined values are sent to `@bind` as a single tuple.

`render_function` is a function that you write yourself, take a look at the examples below.

# Examples

## 🐶 & 🐱

We use the [`do` syntax](https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments) to write our `render_function`. The `Child` function is wrapped around each input, to let `combine` know which values to combine.

```julia
@bind values PlutoUI.combine() do Child
md""\"
# Hi there!

I have \$(
Child(Slider(1:10))
) dogs and \$(
Child(Slider(5:100))
) cats.

Would you like to see them? \$(Child(CheckBox(true)))
""\"
end

values == (1, 5, true) # (initially)
```


> The output looks like:
>
> ![screenshot of running the code above inside Pluto](https://user-images.githubusercontent.com/6933510/145589918-25a3c732-c02e-482b-831b-06131b283597.png)

## 🎏


The `combine` function is most useful when you want to generate your input elements _dynamically_. This example uses [HypertextLiteral.jl](https://github.com/MechanicalRabbit/HypertextLiteral.jl) for the `@htl` macro:

```julia
function wind_speeds(directions)
PlutoUI.combine() do Child
@htl(""\"
<h6>Wind speeds</h6>
<ul>
\$([
@htl("<li>\$(name): \$(Child(Slider(1:100)))</li>")
for name in directions
])
</ul>
""\")
end
end

@bind speeds wind_speeds(["North", "East", "South", "West"])

speeds == (1, 1, 1, 1) # (initially)

# after moving the sliders:
speeds == (100, 36, 73, 60)
```

> The output looks like:
>
> ![screenshot of running the code above inside Pluto](https://user-images.githubusercontent.com/6933510/145588612-14824654-5c73-45f8-983c-8913c7101a78.png)


# Named variant

In the last example, we used `Child` to wrap around contained inputs:
```julia
Child(Slider(1:100))
```
We can also use the **named variant**, which looks like:
```julia
Child("East", Slider(1:100))
```

When you use the *named variant* for all children, **the bound value will be `NamedTuple`, instead of a `Tuple`**.

Let's rewrite our example to use the *named variant*:

```julia
function wind_speeds(directions)
PlutoUI.combine() do Child
@htl(""\"
<h6>Wind speeds</h6>
<ul>
\$([
@htl("<li>\$(name): \$(Child(name, Slider(1:100)))</li>")
for name in directions
])
</ul>
""\")
end
end

@bind speeds wind_speeds(["North", "East", "South", "West"])

speeds == (North=1, East=1, South=1, West=1) # (initially)

# after moving the sliders:
speeds == (North=100, East=36, South=73, West=60)

md"The Eastern wind speed is \$(speeds.East)."
```


> The output looks like:
>
> ![screenshot of running the code above inside Pluto](https://user-images.githubusercontent.com/6933510/145615489-b3fb910d-0dc1-408b-882f-b05ca0129b18.gif)


# Why?
## You can make a new widget!
You can use `combine` to **create a brand new widget** yourself, by combining existing ones!

In the example above, we created a new widget called `wind_speeds`. You could **put this function in a package** (e.g. `WeatherUI.jl`) and people could use it like so:

```julia
import WeatherUI: wind_speeds

@bind speeds wind_speeds(["North", "East"])
```

In other words: you can use `combine` to create application-specific UI elements, and you can put those in your package!

## Difference with repeated `@bind`
The standard way to combine multiple inputs into one output is to use `@bind` multiple times. Our initial example could more easily be written as:
```julia
md""\"
# Hi there!

I have \$(@bind num_dogs Slider(1:10)) dogs and \$(@bind num_cats Slider(5:10)) cats.

Would you like to see them? \$(@bind want_to_see CheckBox(true))
""\"
```

The `combine` function is useful when you are generating inputs **dynamically**, like in our second example. This is useful when:
- The number of parameters is very large, and you don't want to write `@bind parameter1 ...`, `@bind parameter2 ...`, etc.
- The number of parameters is dynamic! For example, you can load in a table in one cell, and then use `combine` in another cell to select which rows you want to use.

"""
combine(f::Function; kwargs...) = _combine(f; kwargs...)
👀 Reading hidden code
1.4 ms
export combine
👀 Reading hidden code
85.5 μs
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
RenderCallback(@htl("hello")) do
nothing
end
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
let
values = []
rc = RenderCallback(@htl("asdf")) do
push!(values, 123)
end
repr(MIME"text/html"(), rc)
disable_callback!(rc)
repr(MIME"text/html"(), rc)
repr(MIME"text/html"(), rc)
values
end
👀 Reading hidden code
---

Examples

md"""
## Examples
"""
👀 Reading hidden code
187 μs
Error message

UndefVarError: _combine not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. combine(f::Function; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    """combine(f::Function; kwargs...) = _combine(f; kwargs...)
  2. Show more...
@skip_as_script @bind values combine() do Child
md"""
# Hi there!

I have $(Child(Slider(1:10))) dogs and $(Child(Slider(5:10))) cats.

Would you like to see them? $(Child(CheckBox(true)))
"""
end
👀 Reading hidden code
---
values (generic function with 5 methods)
@skip_as_script values
👀 Reading hidden code
10.5 μs
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
@skip_as_script @bind speeds combine() do Child
@htl("""
<h3>Wind speeds</h3>
<ul>
$([
@htl("<li>$(name): $(Child(Slider(1:100)))")
for name in ["North", "East", "South", "West"]
])
</ul>
""")
end
👀 Reading hidden code
---
Error message

UndefVarError: speeds not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script speeds
Try asking on Julia Discourse!
@skip_as_script speeds
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
@skip_as_script @bind speeds_named combine() do Child
@htl("""
<h3>Wind speeds</h3>
<ul>
$([
@htl("<li>$(name): $(Child(name, Slider(1:100)))")
for name in ["North", "East", "South", "West"]
])
</ul>
""")
end
👀 Reading hidden code
---
Error message

UndefVarError: speeds_named not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script speeds_named
@skip_as_script speeds_named
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
this suckz 💣
@skip_as_script rb = @bind together combine() do Child
@htl("""
<p>Hello world!</p>
$(Child(TextField()))
$(Child(Slider([sin, cos, tan])))
$([
Child(Scrubbable(7))
for _ in 1:3
])
""")
end
👀 Reading hidden code
---
Error message

UndefVarError: together not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. 	sleep(.5)	togetherend
@skip_as_script let
sleep(.5)
together
end
👀 Reading hidden code
---
Error message

UndefVarError: rb not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script rb
@skip_as_script rb
👀 Reading hidden code
---

Tests

👀 Reading hidden code
163 μs

Initial value & transform

👀 Reading hidden code
177 μs
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
beep boop CRASH 🤖
@skip_as_script begin
it2vs = []
it2b = @bind it2v combine() do Child

z = Child(Slider(1:10))
@htl("""
<p>Hello world!</p>
$(z)
$(Child(Slider([sin, cos, tan])))
$(z)
""")
end
end
👀 Reading hidden code
---
Error message

UndefVarError: it2vs not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script push!(it2vs, it2v)
@skip_as_script push!(it2vs, it2v)
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
this suckz 💣
@skip_as_script begin
itvs = []
itb = @bind itv combine() do Child
@htl("""
<p>Hello world!</p>
$(Child(@htl("<input type=range>")))
$(Child(Slider([sin, cos, tan])))
""")
end
end
👀 Reading hidden code
---
# @skip_as_script itb
👀 Reading hidden code
8.9 μs

Should be [[missing, sin], [50, sin]]

👀 Reading hidden code
230 μs
Error message

UndefVarError: itvs not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script push!(itvs, itv)
Oh no! 🙀
@skip_as_script push!(itvs, itv)
👀 Reading hidden code
---
Error message

UndefVarError: @htl not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. from :0
  2. #macroexpand#51
  3. macroexpand
@skip_as_script begin
☎️s = []
☎️c = combine() do Child
@htl("""
$((
Child(Slider(LinRange(rand(), rand()+1, 10))) for x in 1:5
))
""")
end
☎️b = @bind ☎️ ☎️c
end
👀 Reading hidden code
---
Error message

UndefVarError: ☎️c not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script ☎️c.captured_bonds
uhmmmmmm??!
@skip_as_script ☎️c.captured_bonds
👀 Reading hidden code
---
Error message

UndefVarError: ☎️s not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. @skip_as_script push!(☎️s, ☎️)
@skip_as_script push!(☎️s, ☎️)
👀 Reading hidden code
---

Combine inside combine

md"""
### Combine inside combine
"""
👀 Reading hidden code
174 μs
Error message

UndefVarError: _combine not defined

Stack trace

Here is what happened, the most recent locations are first:

  1. combine(f::Function; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    """combine(f::Function; kwargs...) = _combine(f; kwargs...)
  2. Show more...
Keep calm, you got this!
@skip_as_script cb1 = combine() do Child
md"""
Left: $(Child(:left, Slider(1:10))), right: $(Child(:right, Scrubbable(5)))
"""
end
👀 Reading hidden code
---
Error message

Another cell defining cb1 contains errors.

@skip_as_script cb2b = @bind wowz combine() do Child
md"""
Do a thing: $(Child(CheckBox()))
#### Up
$(Child(cb1))

#### Down
$(Child(cb1))
"""
end
👀 Reading hidden code
---
Error message

Another cell defining cb1 contains errors.

@skip_as_script wowz
👀 Reading hidden code
---
Error message

Another cell defining cb1 contains errors.

@skip_as_script cb2b
👀 Reading hidden code
---